<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        /**
         * 5. 数组的拦截器
         * **/

        class Watcher {
            constructor (vm, expOrfn, cb) {
                this.vm = vm
                this.getter = parsePath(expOrfn)
                this.cb = cb
                this.value = this.get()
            }
            
            get () {
                /* *
                    构造器的get执行 => 当前的watcher实例给window.target => 调用getter方法，收集依赖 push到dep数组 收集了依赖
                * */
                window.target = this
                // 该函数访问对象深层次属性
                let value = this.getter.call(this.vm, this.vm)
                window.target = undefined
                return value
            }

            /* **
                data.a.b.c的值发生变化 => 调用setter 触发notify => 所有依赖项循环调用update => 触发get方法 => 执行cb回调函数，就是(newVal, oldVal) => {} 
            */
            update () {
                const oldValue = this.value
                // 拿到getter里面最新的值,不能再次调用get，否则会把当前watcher再次加入到subs数组中，导致递归死循环
                this.value = this.getter.call(this.vm, this.vm)
                this.cb.call(this.vm, this.value, oldValue)
            }

        }

        const bailRE = /^\w.$/


        /* 
            parsePath的作用是处理对象的深层次属性访问，比如data: {a: {b: {c:}}}要访问data.a.b.c，当把这个传递给watch函数时
            先访问obj[a]，然后obj = obj[a] 再访问obj[a][b] 再是obj[a][b][c]
        */
        function parsePath (path) {
            if (bailRE.test(path)) {
                return
            }
            let segments = path.split('.')
            return function (obj) {
                for (let i = 0; i < segments.length; i++) {
                    if (!obj) return
                    obj = obj[segments[i]]
                }
                return obj
            } 
        }


        /* *
            需要为对象的所有属性都添加响应式，而不是单个属性添加响应式
        * */
        class Observer {
            constructor (value) {
                this.value = value
                // 如果不是数组
                if (!Array.isArray(value)) {
                    this.walk(value)
                }
            }

            walk (obj) {
                const keys = Object.keys(obj) 
                for (let i = 0; i < keys.length; i++) {
                    defineReactive(obj, keys[i], obj[keys[i]])
                }
            }
        }
        
        class Dep {
            constructor () {
                this.subs = []
            }
            addSub (sub) {
                this.subs.push(sub)
            }
            removeSub (sub) {
                this.remove(this.subs, sub)
            }
            depend (sub) {
                // 啥用？
                if (window.target) {
                    this.addSub(window.target)
                }
            }
            notify () {
                // 通知依赖执行
                for (let i = 0; i < this.subs.length; i++) {
                    console.log(this.subs, 'this.subs');
                    this.subs[i].update()
                }
            }
        }

        // 移除函数
        function remove (array, item) {
            if (array.length) {
                let index = array.indexOf(item)
                if (index > -1) {
                    array.splice(index, 1)
                }
            }
        }

        // 改造该函数
        function defineReactive (data, key, val) {
            if (typeof val === 'object') {
                new Observer(val)
            }
            let dep = new Dep()
            Object.defineProperty(data, key, {
                enumerable: true,
                configurable: true,
                get: function () {
                    // 存储依赖
                    dep.depend()
                    return val
                },
                set: function (newVal) {
                    if (val === newVal) {
                        return
                    }
                    val = newVal
                     // 通知依赖
                    dep.notify()
                    console.log(val, 'val');
                }
            })
        }
        let xhg = {
            height: 180
        }
        let data = {
            a: {
                b: {
                    c: '1'
                }
            }
        }
        new Observer(data)
        const watcher = new Watcher(data, 'a.b.c', (newVal, oldVal) => {
            console.log(newVal, 'newVal');
        })
        data.a.b.c = '2'
        // console.log(data, 'data');


        const arrayProto = Array.prototype
        const arrayMethods = Object.create(arrayProto);
        [
            'push',
            'pop',
            'unshift',
            'shift',
            'splice',
            'sort',
            'reverse'
        ].forEach(method =>{
            const original = arrayProto[method]
            Object.defineProperty(arrayMethods, method, {
                // 最终执行的是这个方法
                value: function mutator (...args) {
                    // 执行original，this指向这里
                    return original.apply(this, args)
                },
                enumerable: false,
                writable: true,
                configurable: true
            })
        })
    </script>
</body>
</html>
